#include "Scene.h"

#include <QGraphicsItem>
#include <QPixmap>
#include <QPainter>
#include <QPaintEngine>
#include <QtOpenGL>
#include <QSound>
#include "qmath.h"
#include <QTimer>

#include <AR/ar.h>
#include <AR/video.h>
#include <AR/config.h>
#include <AR/param.h>
#include <AR/gsub_lite.h>

#include "mainwindow.h"
#include "sceneanimation.h"

#include "bubble.h"

using namespace cv;
using namespace std;

static double matrix[16] = {0.0f};
double leftOne = 0.01;
double rightOne = 0.01;
double liftedOne = 0;

static int todelete[100] = {0};
static int toaddto[100][3] = {0};
int counter = 0;
int countnew = 0;
int gameOverCount = 0;
int gametime = 54;
int finalcountdown = gametime;
int wid = 640;
int hei = 480;
int radius = 40;
int leftscore = 0;
int rightscore = 0;
int highscore = 0;
int gamer = 0;

QPen linepen(Qt::black);
QPen linepen2(Qt::black);
QPen powerpen(Qt::red);
QPen bingopen(Qt::green);
QPen scorepen(Qt::red);
QPen countdownpen(Qt::red);

bool leftFire = false;
bool rightFire = false;
bool scoreShow = true;
bool freetoplay = false;
bool shotgunshot = false;
bool epic = false;
bool fireTimer = false;
bool slow = false;
bool moveit = false;
bool player1 = true;


int currentlyplaying = 0;
int playtimeleft = 0;
int yougotit = 0;
int countingleft = 0;
int countingright = 0;

int widthy = 0;
int heighty = 0;
double wratio = 0.0;
double hratio = 0.0;

QPointF argh;

QSound *pop = new QSound("include/sounds/POP.wav");
QSound *bubbling = new QSound("include/sounds/bubbles.wav");
QSound *shot = new QSound("include/sounds/LASER.wav");
QSound *shotgun = new QSound("include/sounds/shotgun.wav");
QSound *wind = new QSound("include/sounds/windyshot.wav");
QSound *alien = new QSound("include/sounds/ALIEN.wav");

/**********************
 * Constructor
 */
CScene::CScene(QObject *parent) : QGraphicsScene(parent)
{
    // mydad = parent;
    StartCapture(wid*2, hei*2);
    m_NyART = new ARToolkitAPI();
    m_NyART->ARInit(wid, hei);
    helper = new SceneAnimation();

    countdown = new QTimer();
    countdown->setInterval(500);
    countdown->setSingleShot(false);
    connect(countdown , SIGNAL(timeout()), this, SLOT(timeOut()));
}
// end CSene();


/**********************
 * Destructor
 */
CScene::~CScene()
{
    pop->~QSound();
    shot->~QSound();
    bubbling->~QSound();
    //    shot->~MediaObject();
    StopCapture();
}
// end ~CScene()


/**********************
 * ..... yeah
 */
void CScene::setHigh(int high){
    highscore = high;
}


/**********************
 * Set one/two plyer modes
 */
void CScene::setPlayer(bool yes){
    player1 = yes;
    leftscore = 0;
    rightscore = 0;
    highscore = 0;
}


/**********************
 * Timer for game - 2 ticks a second
 */
void CScene::timeOut(){
    if(finalcountdown > 0){
        if(playtimeleft > -1) playtimeleft --;
        fireTimer = !fireTimer;
        if(fireTimer) finalcountdown  --;
    }
    if(playtimeleft <= 0 && currentlyplaying == 5){
        slow = false;
        moveit = false;
    }
}


/**********************
 * Controls sounds being played. Presidence counts. Only one sounds can be played at a time
 */
void  CScene::playSound(int whichone, int lengthPlay){
    if(whichone == 7) bubbling->play();
    if((whichone > 3 && currentlyplaying == 1) || playtimeleft <= 0){
        playtimeleft = lengthPlay;
        currentlyplaying = whichone;
        freetoplay = true;
    }
    if(playtimeleft > -1 || playtimeleft == -5){
        if(freetoplay){
            switch (currentlyplaying){
            case 1:
                shot->play();
                break;
            case 2:
                pop->play();
                break;
            case 3:
                bubbling->play();
                break;
            case 4:
                shotgun->play();
                break;
            case 5:
                wind->play();
                slow = true;
                break;
            case 6:
                alien->play();
                break;
            }
            if(playtimeleft == -5) playtimeleft = -1;

            freetoplay = false;
        }
    }

}


/**********************
 * Updates the values from the dynamometers - 0% ~ 100% and over
 */
void CScene::ChangeChildValues(double l, double r){
    leftOne = l/100;
    rightOne = r/100;
    if(leftOne <= 0) leftOne = 0.01;
    if(rightOne <= 0) rightOne = 0.01;
}
// end ChangeChildvalues()


/**********************
 * To show, or not to show
 */
void CScene::showScore(bool checked){
    scoreShow = checked;
}


/**********************
 * Sets game time length
 */
void CScene::gameTime(int times){
    gametime = times + 4;
    finalcountdown = gametime;
}


/**********************
 * Start, stop, and pause controls
 */
void CScene::whatsaHappenin(int changeup){
    if(gamer == 1 && changeup == 1){
        countdown->stop();
        finalcountdown = gametime;
        bubbles.clear();
        gameOverCount = 0;
    }
    gamer = changeup;
    if(gamer == 0 || gamer == 2)
    {
        countdown->stop();
        if(gamer == 0){
            finalcountdown = gametime;
            bubbles.clear();
            gameOverCount = 0;
        }
    }
    if(gamer == 1) {
        countdown->start();
        leftscore = 0;
        rightscore = 0;
    }
}


/**********************
 * Start video feed
 */
bool CScene::StartCapture(int width, int height)
{
    // Start capture
    m_OpenCV.StartCapture(width, height);

    QTimer::singleShot(50, this, SLOT(update()));

    return true;
}
// end StartCapture()


/**********************
 * Stop video feed
 */
bool CScene::StopCapture()
{
    m_OpenCV.StopCapture();
    return true;
}
// end StopCapture()


/**********************
 * draw qdraw stuff
 */
void CScene::drawForeground(QPainter *painter, const QRectF &rect)
{

    if(shotgunshot){
        helper->shotgun(painter, -500, -500);
        shotgunshot = false;
    }
    if(epic){
        helper->waveout(painter, -500, -500);
        epic = false;
    }

    if(gamer == 1 && finalcountdown < (gametime - 3)){
        createBubbles(rect.width(), rect.height());
        animate(rect.height());
        makeBabies();
    }

    argh.setX(rect.width());
    argh.setY(rect.height());
    widthy = argh.x();
    heighty = argh.y();
    wratio = (double) widthy /wid;
    hratio = (double) heighty / hei;

    if(gamer > 0){
        foreach (Bubble *bubble, bubbles) {
            bubble->drawBubble(painter);
        }
    }


    //////////////////////////
    ////// gunsights
    //////////////////////////
    bool isMarkerDetected = false;
    if(m_OpenCV.GetImage() != NULL)
    {
        // Is marker presence on the image?
        isMarkerDetected = m_NyART->FindShit(m_OpenCV.GetImage());

        if(isMarkerDetected)
        {
            int *coor = m_NyART->GetScreenCoor(0);

            ///////////////////////// for model one
            coor[0] = (coor[0]*wratio - widthy/2);
            coor[1] = (heighty - coor[1]*hratio - heighty/2);

            if(countingleft > 10){
                countingleft = 0;
                leftFire = false;
            } else  if (countingleft > 0){
                countingleft ++;
            } else if (countingleft == 0 && leftOne >= 1){
                leftFire = true;
                countingleft ++;
                playSound(1, -5);
                if(gamer == 1 && finalcountdown > 0)
                    checkCollisions(coor, 0, painter);
            }

            drawSight(painter, coor[0], coor[1], leftOne, leftFire);

            ///////////////////////// for model two
            coor = m_NyART->GetScreenCoor(1);
            coor[0] = (coor[0]*wratio - widthy/2);
            coor[1] = (heighty - coor[1]*hratio - heighty/2);

            if(countingright > 8){
                countingright = 0;
                rightFire = false;
            } else  if (countingright > 0){
                countingright ++;
            } else if (countingright == 0 && rightOne >= 1){
                rightFire = true;
                countingright ++;
                playSound(1, -5);
                if(gamer == 1 && finalcountdown > 0)
                    checkCollisions(coor, 1, painter);
            }
            drawSight(painter, coor[0], coor[1], rightOne, rightFire);
        }
    }
    //////////////////////////
    ////// end gunsights
    //////////////////////////

    if(scoreShow) drawScore(painter);
    if(player1){
        emit newScore(0, rightscore);
    } else {

        emit newScore(0, rightscore);
        emit newScore(-1, leftscore);
    }
    drawTimer(painter);

}
// end CSene();


/**********************
 * ..... yeah
 */
void CScene::drawScore(QPainter *painter){
    painter->setPen(scorepen);
    painter->drawText(-(widthy/4) - widthy/8.5, -heighty/2 +14, (player1 ? "HIGH SCORE" : "PLAYER 1:"));
    painter->drawText((widthy/4), -heighty/2 +14, (player1 ? "  SCORE  " : "PLAYER 2:"));
    QFont f ("Courier", 14, QFont::Normal);
    painter->setFont(f);
    painter->drawText(-(widthy/4) - widthy/8.5, -heighty/2 +29, (player1 ? QString::number(highscore) : QString::number(leftscore)));
    painter->drawText((widthy/4), -heighty/2 +29, QString::number(rightscore));

}


/**********************
 * ..... yeah
 */
void CScene::drawTimer(QPainter *painter){
    painter->setPen(scorepen);
    QFont f ("Courier", 14, QFont::Normal);
    painter->setFont(f);
    painter->drawText(-20, -heighty/2 +14, "TIME");

    if(gamer == 0) {
    }
    else if (finalcountdown < (gametime-3))
    {
        if(finalcountdown < 11) painter->setPen(countdownpen);
        QFont c ("Courier", 16, QFont::Bold);
        painter->setFont(c);
        painter->drawText((finalcountdown > 11 ? -10 : -5), -heighty/2 + 30, QString::number(finalcountdown));
        if(finalcountdown == 0)
        {
            countdownpen.setColor(randomColor());
            painter->setPen(countdownpen);
            QFont cdown ("Times", 60, QFont::Bold);
            painter->setFont(cdown);
            painter->drawText(-250, 25, "GAME OVER");
            if(gameOverCount == 0){
                emit gameOver();
                playSound(7, 0);
            }
            if(gameOverCount++ > 200) whatsaHappenin(0);
            if(player1)
                if(rightscore > highscore){
                    highscore = rightscore;
                    emit newScore(1, highscore);
                }

           // playSound(3, 6);
        }
    }
    else if(finalcountdown > (gametime-3))
    {
        countdownpen.setColor(randomColor());
        painter->setPen(countdownpen);
        QFont cdown ("Times", (finalcountdown - (gametime-4)) * 40, QFont::Bold);
        painter->setFont(cdown);
        painter->drawText(-5 - (finalcountdown - (gametime-4)) * 10, (finalcountdown - (gametime-4)) * 20, QString::number(finalcountdown - (gametime-3)));
    }
    else if (finalcountdown > 1)
    {
        countdownpen.setColor(randomColor());
        painter->setPen(countdownpen);
        QFont cdown ("Times", 60, QFont::Bold);
        painter->setFont(cdown);
        painter->drawText(-110, 25, "START");
    }
}

/**********************
 * Creates a random colour
 */
QColor CScene::randomColor()
{
    int red = int(205 + 50.0*qrand()/(RAND_MAX+1.0));
    int green = int(205 + 50.0*qrand()/(RAND_MAX+1.0));
    int blue = int(205 + 50.0*qrand()/(RAND_MAX+1.0));
    int alpha = int(91 + 100.0*qrand()/(RAND_MAX+1.0));

    return QColor(red, green, blue, alpha);
}


/**********************
 * Draws gun sights
 */
void CScene::drawSight(QPainter *painter, int x, int y, double value, bool greens)
{
    painter->save();
    double values = (value < 1? value : 1);
    bingopen.setWidth(4);
    if(greens) painter->setPen(bingopen);
    else painter->setPen(linepen);
    painter->drawPoint(x, y);

    painter->setPen(linepen);
    linepen.setWidth(2);

    painter->translate(x - radius, y - radius);
    painter->drawEllipse(0, 0, int(2*radius), int(2*radius));

    linepen.setWidth(4);
    painter->drawLine(-radius/3, radius, radius - radius/3, radius);
    painter->drawLine(radius, -radius/3, radius, radius - radius/3);
    painter->drawLine(radius + radius/3, radius, 2*radius + radius/3, radius);
    painter->drawLine(radius, radius + radius/3, radius, 2*radius + radius/3);

    if(greens) powerpen.setColor(Qt::green);
    else powerpen.setColor(Qt::red);
    painter->setPen(powerpen);
    powerpen.setWidth(4);
    painter->drawLine(-radius/3, radius, -radius/3 + values*radius, radius);
    painter->drawLine(radius, -radius/3, radius, -radius/3 + values*radius);
    painter->drawLine( 2*radius + radius/3, radius,  2*radius + radius/3 - values * radius, radius);
    painter->drawLine(radius, 2*radius + radius/3, radius, 2*radius + radius/3 - values * radius);

    painter->translate(radius/3, radius/3);

    painter->setPen(linepen2);
    linepen.setWidth(2);
    painter->drawEllipse(0, 0, int(2*radius/1.5), int(2*radius/1.5));
    painter->restore();
}


/**********************
 * Make bubbles!!!!
 */
void CScene::createBubbles(int xsize, int ysize)
{
    if((qrand() % 100) > (93 + wid/xsize * 1.2)){
        bubbles.append(new Bubble(xsize/2, ysize/2, false));
    }
}
// end createBubbles();


/**********************
 * Controls all rendering of the scene
 */
void CScene::drawBackground(QPainter *painter, const QRectF &rect)
{
    // Check if current painter is a OpenGL painter
    if((painter->paintEngine()->type() != QPaintEngine::OpenGL2) &&
            (painter->paintEngine()->type() != QPaintEngine::OpenGL))
    {
        return;
    }

    glLoadIdentity();
    // Displacement trick for exact pixelization
    // Set clear color to white
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    // Grab image from the webcam
    IplImage *cvimage = m_OpenCV.QueryFrame();

    // resize the picture to fill the screen. Used for window resize.
    if(cvimage != NULL)
    {
        IplImage *cvimage2;
        if(finalcountdown < 0){
            cvimage2 = cvCreateImage(cvSize(rect.width(), rect.height()), cvimage->depth, 1);
            cvResize(cvimage, cvimage2,CV_INTER_LINEAR);
            cvCvtColor(cvimage2, cvimage2,CV_RGB2GRAY);
        } else {
            cvimage2 = cvCreateImage(cvSize(rect.width(), rect.height()), cvimage->depth, cvimage->nChannels);
            cvResize(cvimage, cvimage2, CV_INTER_LINEAR);
        }
        glDrawPixels(cvimage2->width, cvimage2->height, GL_RGB, GL_UNSIGNED_BYTE, cvimage2->imageData);
        cvReleaseImage(&cvimage2);
        cvimage2 = NULL;
        free(cvimage2);
    }
    // Timer will update our scene in 50 milliseconds
    QTimer::singleShot(50, this, SLOT(update()));
}
// end drawBackground()


/**********************
 * gets differences of translation, and other things (later) of object passed to it.
 */
void CScene::goHome(){

}
//end goHome()

void CScene::makeBabies(){
    int pos = 1;
    for(int i = 0; i < countnew; i++){
        if(toaddto[i][2] > 1){
            qsrand(toaddto[i][0] + toaddto[i][1]);
            for(int j = 0; j < ((toaddto[i][2]-1) * 2); j ++){
                int size = (toaddto[i][2] - 1) * 60;

                int x  = qrand() % size - (size/2);
                int y = qrand() % size - (size/2);
                Bubble *newone = new Bubble(x - size, y - size, true);
                newone->setSize(toaddto[i][0] + x, toaddto[i][1] + y, toaddto[i][2] - 1);
                bubbles.append(newone);
                pos++;
            }
            pos = 0;
        }
    }
    countnew = 0;
}

/**********************
 * Checks for collisions, and handles them
 */
void CScene::checkCollisions(int *coor, int which, QPainter *painter){
    int a = 0;
    int b = 0;



    foreach (Bubble *bubble, bubbles){
        a = coor[0] - bubble->location[0];
        a = a*a;
        b = coor[1] - bubble->location[1];
        b = b*b ;
        if(qSqrt(a + b) <= bubble->radius){
            deleteBubbles(bubbles.indexOf(bubble));
            playSound(2, -5);
            if(bubble->special > 0){
                yougotit = bubble->special;
            } else {
                if(player1){
                    rightscore += (40 - bubble->size) *10;
                } else {
                    if(which == 0){
                        leftscore += (40 - bubble->size) *10;
                    } else {
                        rightscore += (40 - bubble->size) *10;
                    }
                }
            }
            toaddto[countnew][0] = bubble->location[0];
            toaddto[countnew][1] = bubble->location[1];
            toaddto[countnew][2] = bubble->size;
            countnew ++;
        }
    }
    if(counter > 0){
        deleteBubbles(-2);
    }
    if(yougotit > 0 ){
        if(yougotit == 1){
            epic = true;
            playSound(6, 2);
            foreach (Bubble *bubble, bubbles){
                deleteBubbles(bubbles.indexOf(bubble));
                toaddto[countnew][0] = bubble->location[0];
                toaddto[countnew][1] = bubble->location[1];
                toaddto[countnew][2] = bubble->size;
                countnew ++;
                deleteBubbles(-2);
            }
        } else if (yougotit == 2){
            playSound(4, 2);
            shotgunshot = true;
            foreach (Bubble *bubble, bubbles){
                a = coor[0] - bubble->location[0];
                a = a*a;
                b = coor[1] - bubble->location[1];
                b = b*b ;
                if((a + b) <= 62500)
                    deleteBubbles(bubbles.indexOf(bubble));
            }
            deleteBubbles(-2);
        } else if (yougotit == 3){
            playSound(5, 16);
            deleteBubbles(-2);
        }
        yougotit = 0;
    }
    if(shotgunshot) helper->shotgun(painter, coor[0], coor[1]);
    if(epic) helper->waveout(painter, coor[0], coor[1]);
}
// end checkCollisions()


/**********************
 * Simple method that
 */
void  CScene::deleteBubbles(int toadd){
    if(toadd > -1){
        todelete[counter] = toadd;
        counter++;
    } else {
        for(int q = counter - 1; q > -1; q--){
            bubbles.removeAt(todelete[q]);
            todelete[q] = 0;
        }
        counter = 0;
    }
}
// end deleteBubbles


/**********************
 * Animates the bubbles, and deletes them if they are off screen
 */
void CScene::animate(int heights)
{
    if(slow) moveit = !moveit;
    foreach (Bubble *bubble, bubbles) {

        if(slow){ if(moveit) bubble->move();}
        else bubble->move();

        if(bubble->location[1] < (-heights/2 - 50)){
            deleteBubbles(bubbles.indexOf(bubble));
            if(finalcountdown > 0){
                if(player1) {
                    rightscore -= bubbles.size();
                } else {
                    leftscore -= bubbles.size();
                    rightscore -= bubbles.size();
                }
            }
        }
    }
    if(counter > 0){
        deleteBubbles(-2);
    }
}
// end animate;

